
/*
 * @author kanghua(kanghua151@msn.com) 
*/

#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <glib.h>
#include <string.h>
#include "hlfs.h"
#include "comm_define.h"
#include "misc.h"
#include "logger.h"

#if 1 /* 0*/ 
static int create_new_segfile(void *storage_handler, uint32_t segno) 
{
	int ret = 0;
    	gchar * path = (gchar*)storage_handler;
    	char *segfile_name = build_segfile_name(segno);
    	char *segfile_path = g_build_filename(path, segfile_name, NULL);
    	if (-1 == g_creat(segfile_path,0700)) {
       		ret = -1;
       		goto out;
    	}
out:
    	g_free(segfile_name);
    	g_free(segfile_path);
    	return ret;
}
#endif 

int hlfs_write(struct hlfs_ctrl *ctrl, char *write_buf, uint32_t write_len, uint64_t pos)
{
    int ret = 0;
    char *datablocks;
    guint32 BLOCKSIZE = ctrl->sb.block_size;

    if(pos/BLOCKSIZE == (pos+write_len)/BLOCKSIZE){
        g_message("%s - only need to write part in one block:%llu\n",__func__,pos/BLOCKSIZE);
        datablocks = (char*)g_malloc0(BLOCKSIZE);
        if (!datablocks) {
            g_message("%s -- allocate error!\n", __func__);
            return -1;
        }
        char *block=NULL;

        if(1==(ret=load_block_by_addr(ctrl,pos,&block))){
            g_message("%s - fail to load block for addr %llu\n",__func__,pos);
            block = (char*)g_malloc0(BLOCKSIZE);
            if (!block) {
                g_message("%s -- allocate error!\n", __func__);
                return -1;
            }
        }else if( -1 == ret){
            g_message("can read block:%ld\n",pos/BLOCKSIZE);
            return -1;
        }
        memcpy(datablocks,block,pos%BLOCKSIZE);
        memcpy(datablocks + pos%BLOCKSIZE,write_buf,write_len);
        memcpy(datablocks + (pos+write_len)%BLOCKSIZE,
                block+(pos+write_len)%BLOCKSIZE,
                BLOCKSIZE-(pos+write_len)%BLOCKSIZE);
        g_free(block);
        goto write_log;
    }
    if(pos % BLOCKSIZE != 0 ){
        datablocks = (char*)g_malloc0((write_len/BLOCKSIZE+ 2)*BLOCKSIZE);
        if (!datablocks) {
            g_message("%s -- allocate error!\n", __func__);
            return -1;
        }
        char *first_block;
        if(1==(ret =load_block_by_addr(ctrl,pos,&first_block))){
            g_message("%s - fail to load first block \n",__func__);
            first_block = (char*)g_malloc0(BLOCKSIZE);
            if (!first_block) {
                g_message("%s -- allocate error!\n", __func__);
                return -1;
            }
        }else if(ret == -1){
            g_message("can read block:%ld\n",pos/BLOCKSIZE);
            return -1;
        }
        memcpy(datablocks,first_block,BLOCKSIZE);
        memcpy(datablocks+pos%BLOCKSIZE,write_buf,write_len);
        g_free(first_block);
    }else{
        g_message("%s - do not need load first block \n",__func__);
        datablocks = (char*)g_malloc0((((write_len - 1)/BLOCKSIZE) + 1) * BLOCKSIZE);
        if (!datablocks) {
            g_message("%s -- allocate error!\n", __func__);
            return -1;
        }
        memcpy(datablocks, write_buf, write_len);
    }
    if((pos +write_len)%BLOCKSIZE !=0){
        char *last_block;
        if(1==(ret=load_block_by_addr(ctrl,pos,&last_block))){
            g_message("%s - fail to load last block \n",__func__);
            last_block = (char*)g_malloc0(BLOCKSIZE);
            if (!last_block) {
                g_message("%s -- allocate error!\n", __func__);
                return -1;
            }
        }else if(-1==ret){
            g_message("can read block:%ld\n",pos/BLOCKSIZE);
            return -1;
        }
        memcpy(datablocks+pos%BLOCKSIZE + write_len,
                last_block+(write_len+pos)%BLOCKSIZE,
                BLOCKSIZE-(write_len+pos)%BLOCKSIZE);
    }

write_log:;    
          uint32_t db_start = pos/BLOCKSIZE;
          uint32_t db_end   = (pos+write_len-1)/BLOCKSIZE;
          g_message("%s - db_start:%u db_end:%u\n",__func__,db_start,db_end);

#if 1    
          if(ctrl->inode.length < (pos + write_len)){ 
              ctrl->inode.length = pos + write_len;
          }
          ctrl->inode.mtime  = get_current_time();
          ctrl->inode.ctime  = get_current_time();
          ctrl->inode.atime  = get_current_time();
#endif 

#if 1
        int expand_size =  (db_end-db_start + 1)*BLOCKSIZE + 
                           ib_amount(db_start,db_end) * BLOCKSIZE + 
                           LOG_HEADER_LENGTH + 
                           sizeof(struct inode) + 
                           sizeof(struct inode_map_entry);

        if (ctrl->last_offset + expand_size > ctrl->sb.seg_size) {
            ctrl->last_segno++;
            ctrl->last_offset = 0;
        }
        g_message("%s - last segno:%lld last offset:%llu\n",__func__,ctrl->last_segno,ctrl->last_offset);
#endif

          int size = append_log(ctrl,datablocks,db_start,db_end);
          if(size < 0){
              g_message("fail to append log\n");
              return -1; 
          }
          g_free(datablocks);
          ctrl->last_offset += size;
          g_message("%s - last offset:%llu fs length:%lld\n",__func__,ctrl->last_offset,ctrl->inode.length);
          return write_len;
}
